package ClassData;

import ClassData.Visitor.IVisitor;
import ClassData.Visitor.IVisitorElement;

/**
 * 
 * @note changed by Dominik Ollesz
 */
public class MethodInfo implements IVisitorElement {

	public static final int ACC_PUBLIC   		= 0x0001; //  Declared public; may be accessed from outside its package.  
	public static final int ACC_PRIVATE  		= 0x0002; //  Declared private; accessible only within the defining class.  
	public static final int ACC_PROTECTED  		= 0x0004; //  Declared protected; may be accessed within subclasses.  
	public static final int ACC_STATIC  		= 0x0008; //  Declared static.  
	public static final int ACC_FINAL  			= 0x0010; //  Declared final; may not be overridden.  
	public static final int ACC_SYNCHRONIZED	= 0x0020; //  Declared synchronized; invocation is wrapped in a monitor lock.  
	public static final int ACC_NATIVE  		= 0x0100; //  Declared native; implemented in a language other than Java.  
	public static final int ACC_ABSTRACT  		= 0x0400; //  Declared abstract; no implementation is provided.  
	public static final int ACC_STRICT  		= 0x0800; //  Declared strictfp; floating-point mode is FP-strict  
	
	short access_flags;
	short name_index;
	short descriptor_index;
	
	byte [] parameters;
	
	String parameterTypesAsString;
	
	ConstantPoolUtf8_Info methodName;
	ConstantPoolUtf8_Info methodDescriptor;
	
	AttributeInfo atributeList[];

	/**
	 *  
	 * @param accesFlags acces flags of this method (private, public, static....)
	 * @param nameIndex index to method name in constant pool
	 * @param descriptorIndex index to this method descriptor
	 * 
	 */		
	public MethodInfo(short accesFlags,short nameIndex,short descriptorIndex) {
		access_flags = accesFlags;
		name_index = nameIndex;
		descriptor_index = descriptorIndex;
	}

	public void setAttributeList(AttributeInfo [] newAtributeList) {
		atributeList=newAtributeList;	
	}

	public void setMethodName(ConstantPoolUtf8_Info methodName) {
		this.methodName=methodName;
	}

	public String getMethodName() {
		return(methodName.getString());
	}

	public void setMethodDescriptor(ConstantPoolUtf8_Info methodDescriptor) {
		this.methodDescriptor=methodDescriptor;
	}

	public short getNameIndex() {
		return(name_index);
	}

	public short getDescriptorIndex() {
		return(descriptor_index);
	}

	public int getMaxStack() {
		int counter;
		
		for (counter=0;counter<atributeList.length;counter++) {
			if (atributeList[counter].getClass().getName().equals(AttributeInfo_Code.class.getName())) {
				AttributeInfo_Code tmpCode;
				tmpCode=(AttributeInfo_Code)atributeList[counter];
				return(tmpCode.getMaxStack());
			}
		}
		//cpInfo.getClass().getName().equals(ConstantPoolClass_Info.class.getName())
		return(0);	
	}

	public int getMaxLocals() {
		int counter;
		
		for (counter=0;counter<atributeList.length;counter++) {
			if (atributeList[counter].getClass().getName().equals(AttributeInfo_Code.class.getName())) {
				AttributeInfo_Code tmpCode;
				tmpCode=(AttributeInfo_Code)atributeList[counter];
				return(tmpCode.getMaxLocals());
			}
		}
		//cpInfo.getClass().getName().equals(ConstantPoolClass_Info.class.getName())
		return(0);	
	}

	public byte[] getCode() {
		//int codeSize=0;
		int counter;
		
		for (counter=0;counter<atributeList.length;counter++) {
			if (atributeList[counter].getClass().getName().equals(AttributeInfo_Code.class.getName())) {
				AttributeInfo_Code tmpCode;
				tmpCode=(AttributeInfo_Code)atributeList[counter];
				return(tmpCode.getCode());
			}
		}
		//cpInfo.getClass().getName().equals(ConstantPoolClass_Info.class.getName())
		return(null);	
	}

	public int getCodeSize() {
		int codeSize=0;
		int counter;
		
		for (counter=0;counter<atributeList.length;counter++) {
			if (atributeList[counter].getClass().getName().equals(AttributeInfo_Code.class.getName())) {
				AttributeInfo_Code tmpCode;
				tmpCode=(AttributeInfo_Code)atributeList[counter];
				return(tmpCode.getCodeSize());
			}
		}
		//cpInfo.getClass().getName().equals(ConstantPoolClass_Info.class.getName())
		return(codeSize);
	}

	public short getFlags() {
		return(access_flags);
	}

	/**
	 * @author Dominik Ollesz
	 */
	public int getExceptionsSize() {
		for (AttributeInfo attributeInfo : atributeList) {
			if (attributeInfo.getClass().getName().equals(AttributeInfo_Exceptions.class.getName())) {
				return((AttributeInfo_Exceptions)attributeInfo).getExcetionsSize();
			}
		}
		return 0;
	}
	
	/**
	 * @author Dominik Ollesz
	 */
	public short[] getExceptions() {
		for (AttributeInfo attributeInfo : atributeList) {
			if (attributeInfo.getClass().getName().equals(AttributeInfo_Exceptions.class.getName())) {
				return((AttributeInfo_Exceptions)attributeInfo).getExceptions();
			}
		}
		return(null);
	}
	
	/**
	 * @author Dominik Ollesz
	 */
	public int getExceptionHandlers() {
		for (AttributeInfo attributeInfo : atributeList) {
			if (attributeInfo.getClass().getName().equals(AttributeInfo_Code.class.getName())) {
				return ((AttributeInfo_Code)attributeInfo).getExceptionHandlers();
			}
		}
		return 0;
	}
	
	/**
	 * @author Dominik Ollesz
	 */
	public ExceptionHandler[] getExceptionHandler() {
		for (AttributeInfo attributeInfo : atributeList) {
			if (attributeInfo.getClass().getName().equals(AttributeInfo_Code.class.getName())) {
				return ((AttributeInfo_Code)attributeInfo).getExceptionHandler();
			}
		}
		return(null);
	}
	
	public String getFlagsAsString() {
	
		String returnString;
		
		returnString="";
		
		if ((access_flags & ACC_PUBLIC) > 0) {
			returnString="public";
		}
		
		if ((access_flags & ACC_PRIVATE) > 0) {
			if (returnString.length() > 0) {
				returnString+=", ";
			}
			returnString+="private";
		}
	
		 if ((access_flags & ACC_PROTECTED) > 0) {
			 if (returnString.length() > 0){
				returnString+=", ";
			 }
			 returnString+="protected";
		}
	
		if ((access_flags & ACC_STATIC) > 0) {
			if (returnString.length() > 0) {
				returnString+=", ";
			}
			returnString+="static";
		}
	
		if ((access_flags & ACC_FINAL) > 0) {
			if (returnString.length() > 0) {
				returnString+=", ";
			}
			returnString+="final";
		}
	
		if ((access_flags & ACC_SYNCHRONIZED) > 0) {
			if (returnString.length() > 0) {
				returnString+=", ";
			}
			returnString+="synchronized";
		}
	
		if ((access_flags & ACC_NATIVE) > 0) {
			if (returnString.length() > 0) {
				returnString+=", ";
			}
			returnString+="native";
		}
	
		if ((access_flags & ACC_ABSTRACT) > 0) {
			if (returnString.length() > 0) {
				returnString+=", ";
			}
			returnString+="abstract";
		}
		 
		if ((access_flags & ACC_STRICT) > 0){
			if (returnString.length() > 0) {
				returnString+=", ";
			}
			returnString+="strict";
		}
		 
		return(returnString);
	}

	public void parseParameterTypes(ConstantPool myConstantPool) {
		ConstantPoolUtf8_Info typeDescriptor;
			
		String returnParam;
		String callParams;
		String typeAsString;
		int index;
	
		if (myConstantPool.get(descriptor_index).getClass().getName().equals(ConstantPoolUtf8_Info.class.getName())) {
			typeDescriptor=(ConstantPoolUtf8_Info)myConstantPool.get(descriptor_index);
	
			parameterTypesAsString=typeDescriptor.getString();
			
			System.out.print("Parameters : "+parameterTypesAsString);
			
			index=0;
			while (parameterTypesAsString.charAt(index)!=')') {
				index++;
			}
			returnParam=parameterTypesAsString.substring(index+1);
			typeAsString=parameterTypesAsString.substring(1,index);
	
			//callParams=typeAsString.split(";");
			
			callParams="";
			
			index=0;
			//parameters[parameters.length-1]=(byte) returnParam.charAt(0);
			while (index<typeAsString.length()) {
				switch(typeAsString.charAt(index)) {
					case 'B':
						callParams+='B';
						index++;
						break;
						
					case 'C':
						callParams+='C';
						index++;
						break;
		
					case 'F':
						callParams+='F';
						index++;
						break;
		
					case 'I':
						callParams+='I';
						index++;
						break;
		
					case 'S':
						callParams+='S';
						index++;
						break;
		
					case 'Z':
						callParams+='Z';
						index++;
						break;
		
					case 'D':
						callParams+='D';
						index++;
						break;
		
					case 'J':
						callParams+='J';
						index++;
						break;
			
					case '[':
						callParams+='[';
						
						while (typeAsString.charAt(index)=='[') {
							index++;
						}
						if (typeAsString.charAt(index)=='L') {
							index=typeAsString.indexOf(';', index)+1;
						} else {
							index++;
						}
						break;
						
					case 'L':
						callParams+='L';
						index=typeAsString.indexOf(';', index)+1;
						break;
	
				} // switch
			} // while
			callParams+=returnParam.charAt(0);
				
			System.out.println("Parsed : "+callParams);
			parameters= new byte[callParams.length()];
			for (index=0;index<callParams.length();index++) {
				parameters[index]=(byte)callParams.charAt(index);
			}
		} else {
			System.out.println("Descriptor Error !!!!");
			// TODO Exception
		}
	
		//DescriptorIndex
	}

	public byte[] getParameterList() {
		return(parameters);
	}

	public int getParameterCount() {
		return(parameters.length);
	}

	public String getParametersAsString() {
		return(parameterTypesAsString);
	}

	public boolean isPublic() {
		if ((access_flags & ACC_PUBLIC) > 0)
			return(true);
		
		return(false);	
	}	

	public boolean isPrivate() {
		if ((access_flags & ACC_PRIVATE) > 0)
			return(true);
		
		return(false);	
	}

	public boolean isProtected() {
		if ((access_flags & ACC_PROTECTED) > 0)
			return(true);
		
		return(false);	
	}

	public boolean isStatic() {
		if ((access_flags & ACC_STATIC) > 0)
			return(true);
		
		return(false);	
	}

	public boolean isFinal() {
		if ((access_flags & ACC_FINAL) > 0)
			return(true);
		
		return(false);
	}

	public boolean isSynchronized() {
		if ((access_flags & ACC_SYNCHRONIZED) > 0)
			return(true);
		
		return(false);	
	}

	public boolean isNative() {
		if ((access_flags & ACC_NATIVE) > 0)
			return(true);
		
		return(false);
	}

	public boolean isStrict() {
		if ((access_flags & ACC_ABSTRACT) > 0)
			return(true);
		
		return(false);	
	}

	public boolean isAbstract() {
		if ((access_flags & ACC_STRICT) > 0)
			return(true);
		
		return(false);
	}

	@Override
	public void receive_Visitor(IVisitor visitor) {
		visitor.visit_MethodInfo(this);
	}
	
}
